Imports System 
Imports System.Collections.Generic 
Imports System.Linq 
Imports System.Text 
Imports System.Windows 
Imports System.Windows.Controls 
Imports System.Windows.Data 
Imports System.Windows.Documents 
Imports System.Windows.Input 
Imports System.Windows.Media 
Imports System.Windows.Media.Imaging 
Imports System.Windows.Navigation 
Imports System.Windows.Shapes 
Imports System.Data 
Imports System.Windows.Media.Effects 
Imports System.Text.RegularExpressions 

Namespace SimpleChart.Charts 
    ''' <summary> 
    ''' Interaction logic for BarChart.xaml 
    ''' </summary> 
    Public Partial Class BarChart 
        Inherits UserControl 
        Private m_xAxisText As String 
        
        Private horizontalGridLineThickness As Double 
        
        Private defaultXYalueFontSize As Double = 8 
        Private defaultYValueFontSize As Double = 7 
        Private maxData As System.Nullable(Of Double) 
        
        Private left As Double = 5 
        Private top As Double 
        
        Private topMargin As Double = 40 
        Private leftMargin As Double = 40 
        Private bottomMargin As Double = 100 
        
        Private spaceBetweenBars As Double = 8 
        
        Private barRow As DataRow 
        ' to hold current row 
        Private prevBrush As Brush 
        
        Private m_legendTextColor As Brush 
        
        Private txtXAxis As TextBlock 
        Private txtTopTitle As TextBlock 
        Private bubbleText As TextBlock 
        
        Private m_gridLineColor As Color = Colors.LightGray 
        
        Private m_legends As New List(Of Legend)() 
        
        Public Event BarClickHandler As EventHandler(Of BarEventArgs) 
        
Private _ShowValueOnBar As Boolean 
        Public Property ShowValueOnBar() As Boolean 
            Get 
                Return _ShowValueOnBar 
            End Get 
            Set(ByVal value As Boolean) 
                _ShowValueOnBar = value 
            End Set 
        End Property 
Private _SmartAxisLabel As Boolean 
        Public Property SmartAxisLabel() As Boolean 
            Get 
                Return _SmartAxisLabel 
            End Get 
            Set(ByVal value As Boolean) 
                _SmartAxisLabel = value 
            End Set 
        End Property 
        
        Public ReadOnly Property Legends() As List(Of Legend) 
            Get 
                Return m_legends 
            End Get 
        End Property 
        
Private _BarWidth As Double 
        Public Property BarWidth() As Double 
            Get 
                Return _BarWidth 
            End Get 
            Set(ByVal value As Double) 
                _BarWidth = value 
            End Set 
        End Property 
Private _Title As String 
        Public Property Title() As String 
            Get 
                Return _Title 
            End Get 
            Set(ByVal value As String) 
                _Title = value 
            End Set 
        End Property 
Private _ToolTipText As String 
        Public Property ToolTipText() As String 
            Get 
                Return _ToolTipText 
            End Get 
            Set(ByVal value As String) 
                _ToolTipText = value 
            End Set 
        End Property 
Private _XAxisField As String 
        Public Property XAxisField() As String 
            Get 
                Return _XAxisField 
            End Get 
            Set(ByVal value As String) 
                _XAxisField = value 
            End Set 
        End Property 
Private _EnableZooming As Boolean 
        Public Property EnableZooming() As Boolean 
            Get 
                Return _EnableZooming 
            End Get 
            Set(ByVal value As Boolean) 
                _EnableZooming = value 
            End Set 
        End Property 
        
Private _QueryParam As String 
        Public Property QueryParam() As String 
            Get 
                Return _QueryParam 
            End Get 
            Set(ByVal value As String) 
                _QueryParam = value 
            End Set 
        End Property 
        
Private _GridLineColor As Color 
        Public Property GridLineColor() As Color 
            Get 
                Return _GridLineColor 
            End Get 
            Set(ByVal value As Color) 
                _GridLineColor = value 
            End Set 
        End Property 
        
        Public Property XAxisText() As String 
            Get 
                Return m_xAxisText 
            End Get 
            Set(ByVal value As String) 
                m_xAxisText = value 
                txtXAxis.Text = value 
            End Set 
        End Property 
        
        
Private _ValueField As List(Of String) 
        Public Property ValueField() As List(Of String) 
            Get 
                Return _ValueField 
            End Get 
            Set(ByVal value As List(Of String)) 
                _ValueField = value 
            End Set 
        End Property 
        
        Public Property TextColor() As Brush 
            Get 
                Return bubbleText.Foreground 
            End Get 
            Set(ByVal value As Brush) 
                bubbleText.Foreground = value 
                txtTopTitle.Foreground = value 
                txtXAxis.Foreground = value 
            End Set 
        End Property 
        
Private _GridLineHorizontalThickness As Double 
        Public Property GridLineHorizontalThickness() As Double 
            Get 
                Return _GridLineHorizontalThickness 
            End Get 
            Set(ByVal value As Double) 
                _GridLineHorizontalThickness = value 
            End Set 
        End Property 
        
Private _ShowHorizontalGridLine As Boolean 
        Public Property ShowHorizontalGridLine() As Boolean 
            Get 
                Return _ShowHorizontalGridLine 
            End Get 
            Set(ByVal value As Boolean) 
                _ShowHorizontalGridLine = value 
            End Set 
        End Property 
        
        Public Property BackGroundColor() As Brush 
            Get 
                Return Me.Background 
            End Get 
            Set(ByVal value As Brush) 
                chartArea.Background = value 
                Me.Background = value 
            End Set 
        End Property 
        
Private _LegendTextColor As Brush 
        Public Property LegendTextColor() As Brush 
            Get 
                Return _LegendTextColor 
            End Get 
            Set(ByVal value As Brush) 
                _LegendTextColor = value 
            End Set 
        End Property 
        
        
Private _DataSource As DataSet 
        Public Property DataSource() As DataSet 
            Get 
                Return _DataSource 
            End Get 
            Set(ByVal value As DataSet) 
                _DataSource = value 
            End Set 
        End Property 
        
        Public Sub New() 
            InitializeComponent() 
            
            
            
            ValueField = New List(Of String)() 
            InitChartControls() 
            BarWidth = 60 
            horizontalGridLineThickness = 0.3 
            
            m_legendTextColor = New SolidColorBrush(Parser.GetDarkerColor(Colors.Yellow, 10)) 
            
            
            Dim gsc As New GradientStopCollection(2) 
            gsc.Add(New GradientStop(Colors.Black, 1)) 
            gsc.Add(New GradientStop(Colors.Gray, 0)) 
            
                
            chartArea.Background = New LinearGradientBrush(gsc, 90) 
        End Sub 
        
        ''' <summary> 
        ''' Get max value data element. 
        ''' </summary> 
        ''' <param name="dt"></param> 
        ''' <returns></returns> 
        Private Function GetMax(ByVal dt As DataTable) As System.Nullable(Of Double) 
            Dim max As System.Nullable(Of Double) = 0 
            Dim tmp As System.Nullable(Of Double) = 0 
            
            
            For Each valField As String In ValueField 
                For Each r As DataRow In dt.Rows 
                    If Not r.Table.Columns.Contains(valField) Then 
                        Continue For 
                    End If 
                    If Not r(valField) Is System.DBNull.Value Then
                        tmp = Convert.ToDouble(r(valField))

                        If tmp > max Then
                            max = tmp
                        End If
                    End If
                Next 
            Next 
            
            max = If((max IsNot Nothing), max, 0) 
            
            Return max 
        End Function 
        
        ''' <summary> 
        ''' Reset the value field. 
        ''' </summary> 
        Public Sub Reset() 
            ValueField.Clear() 
        End Sub 
        
        ''' <summary> 
        ''' Creates the chart based on the datasource. 
        ''' </summary> 
        Public Sub Generate() 
            Try 
                m_legends.Clear() 
                chartArea.Children.Clear() 
                
                ' Setup chart elements. 
                AddChartControlsToChart() 
                
                ' Setup chart area. 
                SetUpChartArea() 
                
                ' Will be made more generic in the next versions. 
                Dim dt As DataTable = TryCast(DataSource, DataSet).Tables(0) 
                
                If dt IsNot Nothing Then 
                    ' if no data found draw empty chart. 
                    If dt.Rows.Count = 0 Then 
                        DrawEmptyChart() 
                        Exit Sub 
                    End If 
                    
                    ' Hide the nodata found text. 
                    txtNoData.Visibility = Visibility.Hidden 
                    
                    ' Get the max y-value. This is used to calculate the scale and y-axis. 
                    maxData = GetMax(dt) 
                    
                    ' Prepare the chart for rendering. Does some basic setup. 
                    PrepareChartForRendering() 
                    
                    ' Get the total bar count. 
                    Dim barCount As Integer = dt.Rows.Count 
                    
                    ' If more than 1 value field, then this is a group chart. 
                    Dim isSeries As Boolean = ValueField.Count > 1 
                    
                    ' no legends added yet. 
                    Dim legendAdded As Boolean = False 
                    ' no legends yet added. 
                    ' For each row in the datasource 
                    For Each row As DataRow In dt.Rows 
                        ' Draw x-axis label based on datarow. 
                        DrawXAxisLabel(row) 
                        
                        ' Set the barwidth. This is required to adjust the size based on available no. of 
                        ' bars. 
                        SetBarWidth(barCount) 
                        
                        ' For each row the current series is initialized to 0 to indicate start of series. 
                        Dim currentSeries As Integer = 0 
                        
                        ' For each value in the datarow, draw the bar. 
                        For Each valField As String In ValueField 
                            If valField Is Nothing Then 
                                Continue For 
                            End If 
                            
                            If Not row.Table.Columns.Contains(valField) Then 
                                Continue For 
                            End If 
                            
                            ' Draw bar for each value. 
                                
                            DrawBar(isSeries, legendAdded, row, currentSeries, valField) 
                        Next 
                        legendAdded = True 
                        
                        ' Set up location for next bar in series. 
                        If isSeries Then 
                            left = left + spaceBetweenBars 
                        End If 
                    Next 
                    
                    ' Reset the chartarea to accomdodate all the chart elements. 
                    If (left + BarWidth) > chartArea.Width Then 
                        chartArea.Width = left + BarWidth 
                    End If 
                    
                    ' Draw the x-axis. 
                    DrawXAxis() 
                    
                    ' Draw the y-axis. 
                    DrawYAxis() 
                    
                    ' Draw the legend. 
                    DrawLegend() 
                End If 
            Catch ex As Exception 
                ' TODO: Finalize exception handling strategy. 
                MessageBox.Show(ex.Message) 
            End Try 
        End Sub 
        
        ''' <summary> 
        ''' Draws a bar 
        ''' </summary> 
        ''' <param name="isSeries">Whether current bar is in a series or group.</param> 
        ''' <param name="legendAdded">Indicates whether to add legend.</param> 
        ''' <param name="row">The current bar row.</param> 
        ''' <param name="currentSeries">The current series. Used to group series and color code bars.</param> 
        ''' <param name="valField">Value is fetched from the datasource from this field.</param> 
        Private Sub DrawBar(ByVal isSeries As Boolean, ByVal legendAdded As Boolean, ByVal row As DataRow, ByRef currentSeries As Integer, ByVal valField As String) 
            Dim val As Double = 0R 
            
            If Not row(valField) Is DBNull.Value Then
                val = 0
            Else
                val = Convert.ToDouble(row(valField))
            End If
            
            ' Calculate bar value. 
            Dim calValue As System.Nullable(Of Double) = ((CSng(val) * 100 / maxData)) * (chartArea.Height - bottomMargin - topMargin) / 100 
            
            Dim rect As New Rectangle() 
            
            ' Setup bar attributes. 
            SetBarAttributes(calValue, rect) 
            
            ' Color the bar. 
            Dim stroke As Color = Helper.GetDarkColorByIndex(currentSeries) 
            rect.Fill = New SolidColorBrush(stroke) 
            
            ' Setup bar events. 
            SetBarEvents(rect) 
            
            ' Add the legend if not added. 
            If isSeries AndAlso Not legendAdded Then 
                m_legends.Add(New Legend(stroke, ValueField(currentSeries))) 
            End If 
            
            ' Calculate bar top and left position. 
            top = (chartArea.Height - bottomMargin) - rect.Height 
            Canvas.SetTop(rect, top) 
            Canvas.SetLeft(rect, left + leftMargin) 
            
            ' Add bar to chart area. 
            chartArea.Children.Add(rect) 
            
            ' Display value on bar if set to true. 
            If ShowValueOnBar Then 
                DisplayYValueOnBar(val, rect) 
            End If 
            
            ' Create Bar object and assign to the rect. 
            rect.Tag = New Bar(val, row, valField) 
            
            ' Calculate the new left postion for subsequent bars. 
            If isSeries Then 
                left = left + rect.Width 
            Else 
                left = left + BarWidth + spaceBetweenBars 
            End If 
            
            ' Increment the series 
            currentSeries += 1 
        End Sub 
        
        ''' <summary> 
        ''' Setup bar events. 
        ''' </summary> 
        ''' <param name="rect"></param> 
        Private Sub SetBarEvents(ByVal rect As Rectangle) 
            AddHandler rect.MouseLeftButtonUp, AddressOf Bar_MouseLeftButtonUp 
            AddHandler rect.MouseEnter, AddressOf Bar_MouseEnter 
            AddHandler rect.MouseLeave, AddressOf Bar_MouseLeave 
        End Sub 
        
        ''' <summary> 
        ''' Setup bar attributes. 
        ''' </summary> 
        ''' <param name="currentSeries"></param> 
        ''' <param name="calValue"></param> 
        ''' <param name="rect"></param> 
        ''' <returns></returns> 
        Private Sub SetBarAttributes(ByVal calValue As System.Nullable(Of Double), ByVal rect As Rectangle) 
            rect.Width = BarWidth 
            If calValue < 1 Then 
                rect.Height = 1 
            Else 
                rect.Height = calValue.Value 
            End If 
            
            rect.HorizontalAlignment = HorizontalAlignment.Left 
            rect.VerticalAlignment = VerticalAlignment.Center 
                
            rect.StrokeThickness = 1 
        End Sub 
        
        ''' <summary> 
        ''' Display y-value on bar. 
        ''' </summary> 
        ''' <param name="val"></param> 
        ''' <param name="rect"></param> 
        Private Sub DisplayYValueOnBar(ByVal val As Double, ByVal rect As Rectangle) 
            Dim yValue As New TextBlock() 
            yValue.Text = val.ToString() 
            yValue.Width = 80 
            yValue.Foreground = TextColor 
            yValue.HorizontalAlignment = HorizontalAlignment.Center 
            yValue.TextAlignment = TextAlignment.Center 
            yValue.FontSize = defaultYValueFontSize 
            
            AddHandler yValue.MouseEnter, AddressOf yValue_MouseEnter 
            AddHandler yValue.MouseLeave, AddressOf yValue_MouseLeave 
            chartArea.Children.Add(yValue) 
            Canvas.SetTop(yValue, top - 10) 
            Canvas.SetLeft(yValue, left + (rect.Width / 2)) 
        End Sub 
        
        Private Sub SetBarWidth(ByVal barCount As Integer) 
            BarWidth = (chartArea.Width - (spaceBetweenBars * ValueField.Count * barCount) - (leftMargin * 3)) / (barCount * ValueField.Count) 
            
            ' check min bar width 
            If BarWidth > 20 OrElse BarWidth < 20 Then 
                BarWidth = 20 
            End If 
        End Sub 
        
        Private Sub DrawXAxisLabel(ByVal row As DataRow) 
            ' Setup XAxis label 
            Dim markText As New TextBlock() 
            markText.Text = row(XAxisField).ToString() 
            markText.Width = 80 
            'markText.TextTrimming = TextTrimming.CharacterEllipsis; 
            markText.HorizontalAlignment = HorizontalAlignment.Stretch 
            
            markText.Foreground = TextColor 
            'markText.HorizontalAlignment = HorizontalAlignment.Center; 
            markText.TextAlignment = TextAlignment.Center 
            markText.FontSize = 8 
            
            AddHandler markText.MouseEnter, AddressOf XText_MouseEnter 
            AddHandler markText.MouseLeave, AddressOf XText_MouseLeave 
            
            If SmartAxisLabel Then 
                Dim st As Transform = New SkewTransform(0, 20) 
                markText.RenderTransform = st 
            End If 
            
            chartArea.Children.Add(markText) 
            Canvas.SetTop(markText, Me.Height - bottomMargin) 
            ' adjust y location 
            Canvas.SetLeft(markText, left + leftMargin / 2) 
        End Sub 
        
        ''' <summary> 
        ''' Prepares the chart for rendering. Sets up control width and location. 
        ''' </summary> 
        Private Sub PrepareChartForRendering() 
            Canvas.SetTop(txtXAxis, Me.Height - 20) 
            Canvas.SetLeft(txtXAxis, leftMargin) 
            
            txtTopTitle.Width = Me.Width 
            txtTopTitle.FontSize = 14 
            txtTopTitle.Text = Title 
            txtTopTitle.TextAlignment = TextAlignment.Center 
            Canvas.SetTop(txtTopTitle, 0) 
            Canvas.SetLeft(txtTopTitle, leftMargin) 
        End Sub 
        
        ''' <summary> 
        ''' Sets up the chart area with default values 
        ''' </summary> 
        Private Sub SetUpChartArea() 
            If Not EnableZooming Then 
                zoomSlider.Visibility = Visibility.Hidden 
            End If 
            
            If Me.Height.ToString() = "NaN" Then 
                Me.Height = 450 
            End If 
            
            chartArea.Height = Me.Height 
            
            If Me.Width.ToString() = "NaN" Then 
                Me.Width = 800 
            End If 
            
            chartArea.Width = Me.Width 
        End Sub 
        
        ''' <summary> 
        ''' Draws an empty chart. 
        ''' </summary> 
        Private Sub DrawEmptyChart() 
            txtNoData.Visibility = Visibility.Visible 
            Canvas.SetTop(txtNoData, chartArea.Height / 2) 
            Canvas.SetLeft(txtNoData, chartArea.Width / 2) 
        End Sub 
        
        Private Sub yValue_MouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs) 
            Dim tb As TextBlock = TryCast(sender, TextBlock) 
            tb.FontSize = defaultXYalueFontSize 
        End Sub 
        
        Private Sub yValue_MouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs) 
            Dim tb As TextBlock = TryCast(sender, TextBlock) 
            tb.FontSize = 10 
        End Sub 
        
        Private Sub XText_MouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs) 
            Dim tb As TextBlock = TryCast(sender, TextBlock) 
            tb.FontSize = defaultXYalueFontSize 
        End Sub 
        
        Private Sub XText_MouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs) 
            Dim tb As TextBlock = TryCast(sender, TextBlock) 
            tb.FontSize = 10 
        End Sub 
        
        
        ''' <summary> 
        ''' Initialize chart controls. 
        ''' </summary> 
        Private Sub InitChartControls() 
            txtTopTitle = New TextBlock() 
            txtXAxis = New TextBlock() 
            bubbleText = New TextBlock() 
            
            bubbleText.FontSize = 8 
            
            Dim tf As Transform = New ScaleTransform(1.5, 1.5, 12, 24) 
                
                
            bubbleText.RenderTransform = tf 
        End Sub 
        
        ''' <summary> 
        ''' Add chart controls to chart. This creates a basic layout for the chart. 
        ''' </summary> 
        Private Sub AddChartControlsToChart() 
            chartArea.Children.Add(txtXAxis) 
            chartArea.Children.Add(txtTopTitle) 
            chartArea.Children.Add(bubbleText) 
        End Sub 
        
        
        ''' <summary> 
        ''' Draw chart legends. 
        ''' </summary> 
        Private Sub DrawLegend() 
            If m_legends Is Nothing OrElse m_legends.Count = 0 Then 
                Exit Sub 
            End If 
            
            ' Initialize legend location. 
            Dim legendX1 As Double = leftMargin + txtXAxis.Text.Length + 100 
            Dim legendWidth As Double = 20 
            
            ' Draw all legends 
            For Each legend As Legend In m_legends 
                Dim legendShape As New Line() 
                
                legendShape.Stroke = New SolidColorBrush(legend.LegendColor) 
                legendShape.StrokeDashCap = PenLineCap.Round 
                legendShape.StrokeThickness = 8 
                
                legendShape.StrokeStartLineCap = PenLineCap.Round 
                legendShape.StrokeEndLineCap = PenLineCap.Triangle 
                
                
                legendShape.X1 = legendX1 
                legendShape.Y1 = Me.Height - 10 
                legendShape.X2 = legendX1 + legendWidth 
                legendShape.Y2 = Me.Height - 10 
                
                chartArea.Children.Add(legendShape) 
                
                Dim txtLegend As New TextBlock() 
                txtLegend.Text = legend.LegendText 
                txtLegend.Foreground = m_legendTextColor 
                
                chartArea.Children.Add(txtLegend) 
                Canvas.SetTop(txtLegend, Me.Height - 20) 
                Canvas.SetLeft(txtLegend, legendShape.X2 + 2) 
                
                legendX1 += legendWidth + 30 + txtLegend.Text.Length 
            Next 
        End Sub 
        
        
        ''' <summary> 
        ''' Draws XAxis 
        ''' </summary> 
        Private Sub DrawXAxis() 
            ' Draw axis 
            Dim xaxis As New Line() 
            xaxis.X1 = leftMargin 
            xaxis.Y1 = Me.Height - bottomMargin 
            xaxis.X2 = Me.chartArea.Width 
            xaxis.Y2 = Me.Height - bottomMargin 
            
            xaxis.Stroke = New SolidColorBrush(Colors.Silver) 
                
            chartArea.Children.Add(xaxis) 
        End Sub 
        
        ''' <summary> 
        ''' Draws YAxis. Here we use the maxData vlaue calculated earlier. This method also 
        ''' sets up the y-axis marker. 
        ''' </summary> 
        Private Sub DrawYAxis() 
            ' Drawing yaxis is as simple as adding a line control at appropriate location. 
            Dim yaxis As New Line() 
            yaxis.X1 = leftMargin 
            yaxis.Y1 = 0 
            yaxis.X2 = leftMargin 
            yaxis.Y2 = Me.Height - bottomMargin 
            yaxis.Stroke = New SolidColorBrush(Colors.Silver) 
            chartArea.Children.Add(yaxis) 
            
            ' Set the scale factor for y-axis marker. 
            Dim scaleFactor As Double = 10 
            
            ' this value is used to increment the y-axis marker value. 
            Dim yMarkerValue As Double = Math.Ceiling(maxData.Value / scaleFactor) 
            
            ' This value is used to increment the y-axis marker location. 
            Dim scale As Double = 5 
            ' default value 5. 
            ' get the scale based on the current max y value and other chart element area adjustments. 
            scale = ((CSng(yMarkerValue) * 100 / maxData.Value)) * (chartArea.Height - bottomMargin - topMargin) / 100 
            
            Dim y1 As Double = Me.Height - bottomMargin 
            
            Dim yAxisValue As Double = 0 
            
            For i As Integer = 0 To scaleFactor 
                ' Add y-axis marker line chart. 
                Dim marker As Line = AddMarkerLineToChart(y1) 
                
                ' Draw horizontal grid based on marker location. 
                DrawHorizontalGrid(marker.X1, y1) 
                
                ' Add the y-marker to the chart. 
                AddMarkerTextToChart(y1, yAxisValue) 
                
                ' Adjust the top location for next marker. 
                y1 -= scale 
                
                ' Increment the y-marker value. 
                yAxisValue += yMarkerValue 
                
            Next 
        End Sub 
        
        ''' <summary> 
        ''' Add the marker line to chart. 
        ''' </summary> 
        ''' <param name="top">The top location where the marker is to be placed.</param> 
        ''' <returns>The marker line. This is used for drawing the horizontal grid line.</returns> 
        Private Function AddMarkerLineToChart(ByVal top As Double) As Line 
            Dim marker As New Line() 
            marker.X1 = leftMargin - 4 
            marker.Y1 = top 
            marker.X2 = marker.X1 + 4 
            marker.Y2 = top 
            marker.Stroke = New SolidColorBrush(Colors.Red) 
            chartArea.Children.Add(marker) 
            Return marker 
        End Function 
        
        ''' <summary> 
        ''' Add marker text to chart on yaxis. 
        ''' </summary> 
        ''' <param name="top">The top location.</param> 
        ''' <param name="markerTextValue">The marker text value.</param> 
        Private Sub AddMarkerTextToChart(ByVal top As Double, ByVal markerTextValue As Double) 
            Dim markText As New TextBlock() 
            markText.Text = markerTextValue.ToString() 
            markText.Width = 30 
            markText.FontSize = 7 
            markText.Foreground = TextColor 
            markText.HorizontalAlignment = HorizontalAlignment.Right 
            markText.TextAlignment = TextAlignment.Right 
            chartArea.Children.Add(markText) 
            
            Canvas.SetTop(markText, top - 10) 
            ' adjust y location 
            Canvas.SetLeft(markText, leftMargin - 40) 
        End Sub 
        
        ''' <summary> 
        ''' Draw horizontal Grid, if ShowHorizontalGridLine property is set. 
        ''' </summary> 
        ''' <param name="x1">starting left postion</param> 
        ''' <param name="y1">starting top postion</param> 
        Private Sub DrawHorizontalGrid(ByVal x1 As Double, ByVal y1 As Double) 
            If Not ShowHorizontalGridLine Then 
                Exit Sub 
            End If 
            
            Dim gridLine As New Line() 
            gridLine.X1 = x1 
            gridLine.Y1 = y1 
            gridLine.X2 = chartArea.Width 
            gridLine.Y2 = y1 
            
            gridLine.StrokeThickness = horizontalGridLineThickness 
            
            gridLine.Stroke = New SolidColorBrush(GridLineColor) 
            
                
            chartArea.Children.Add(gridLine) 
        End Sub 
        
        Private Sub Bar_MouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs) 
            Dim rect As Rectangle = TryCast(sender, Rectangle) 
            rect.Fill = prevBrush 
                
            prevBrush = Nothing 
        End Sub 
        
        Private Sub Bar_MouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs) 
            Dim rect As Rectangle = TryCast(sender, Rectangle) 
            prevBrush = rect.Fill 
            
            rect.Fill = New SolidColorBrush(Colors.LightGreen) 
            
            Dim b As Bar = TryCast(rect.Tag, Bar) 
            Dim tip As New ToolTip() 
            
            barRow = b.BarRow 
            tip.Content = MatchToolTipTemplate(b.ValueField) 
            
                
            rect.ToolTip = tip 
        End Sub 
        
        ''' <summary> 
        ''' Match tooltip template and replace fields. Need help in improving this. 
        ''' Searches for a tokein in {} and replaces with the actual value from DataSource. 
        ''' Supports only single token replacement. Need to add support for multiple token replacement. 
        ''' </summary> 
        ''' <returns></returns> 
        
        Public Function MatchToolTipTemplate(ByVal valueField As String) As String 
            'string matchExpression = @"{\w+}"; 
            'string matchExpression = @"{field}"; 
            'MatchEvaluator matchField = MatchEvaluatorField; 
            
                
                'return Regex.Replace(ToolTipText, matchExpression, matchField); 
            Return (ToolTipText.Replace("{field}", GetResolvedTemplateValue(valueField))) 
        End Function 
        
        Private Function GetResolvedTemplateValue(ByVal valueField As String) As String 
            Dim newText As String = String.Empty
            Try 
                newText = barRow(valueField).ToString() 
            Catch 
            End Try 
            Return newText 
        End Function 
        
        
        <Obsolete()> _ 
        Private Function MatchEvaluatorField(ByVal m As Match) As String 
            Dim newText As String = m.Value.Replace("{"c, " "c) 
            newText = newText.Replace("}"c, " "c) 
            Try 
                newText = barRow(newText.Trim()).ToString() 
            Catch 
            End Try 
            Return newText 
        End Function 
        
        
        Private Sub Bar_MouseLeftButtonUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs) 
            Dim rect As Rectangle = TryCast(sender, Rectangle) 
            Dim b As Bar = TryCast(rect.Tag, Bar) 
            
                
            RaiseEvent BarClickHandler(Me, New BarEventArgs(b)) 
        End Sub 

       
        Private Sub BarChart_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
            GeneralTasks.ChangeTheme(Me)
            GeneralTasks.EnumerateandFill(Me.LogicalChildren)
        End Sub
    End Class
End Namespace 
